Įvaldykite React createRef imperatyviam DOM ir komponentų egzempliorių valdymui. Sužinokite, kaip jį efektyviai naudoti klasės komponentuose fokusui, medijai ir integracijoms.
React createRef: Išsamus vadovas tiesioginei komponentų ir DOM elementų sąveikai
Plačioje ir dažnai sudėtingoje šiuolaikinio interneto kūrimo srityje „React“ tapo dominuojančia jėga, pirmiausia vertinama dėl savo deklaratyvaus požiūrio į vartotojo sąsajų kūrimą. Ši paradigma skatina programuotojus apibūdinti, kaip jų vartotojo sąsaja turėtų atrodyti remiantis duomenimis, o ne nurodyti, kaip pasiekti tą vizualinę būseną tiesioginėmis DOM manipuliacijomis. Ši abstrakcija ženkliai supaprastino vartotojo sąsajų kūrimą, todėl programos tapo labiau nuspėjamos, lengviau suprantamos ir labai našios.
Tačiau realus interneto programų pasaulis retai būna visiškai deklaratyvus. Yra specifinių, tačiau dažnų scenarijų, kai tiesioginė sąveika su pagrindiniu DOM (Document Object Model) elementu ar klasės komponento egzemplioriumi tampa ne tik patogi, bet ir absoliučiai būtina. Šie „avariniai išėjimai“ iš deklaratyvaus „React“ srauto yra žinomi kaip refs. Tarp įvairių mechanizmų, kuriuos „React“ siūlo šiems ryšiams kurti ir valdyti, React.createRef() yra pamatinė API, ypač aktuali programuotojams, dirbantiems su klasės komponentais.
Šis išsamus vadovas skirtas tapti jūsų galutiniu šaltiniu, padėsiančiu suprasti, įdiegti ir įvaldyti React.createRef(). Mes pradėsime išsamią jo paskirties analizę, gilinsimės į sintaksę ir praktinius pritaikymus, nušviesime geriausias praktikas ir atskirsime jį nuo kitų ref valdymo strategijų. Nesvarbu, ar esate patyręs React programuotojas, siekiantis sustiprinti savo supratimą apie imperatyvias sąveikas, ar naujokas, norintis perprasti šią esminę koncepciją, šis straipsnis suteiks jums žinių, reikalingų kurti tvirtesnes, našesnes ir globaliai prieinamas React programas, kurios elegantiškai susidoroja su sudėtingais šiuolaikinių vartotojų patirčių reikalavimais.
Ref'ų supratimas „React“: Deklaratyvaus ir imperatyvaus pasaulių sujungimas
Iš esmės „React“ palaiko deklaratyvų programavimo stilių. Jūs apibrėžiate savo komponentus, jų būseną ir kaip jie atvaizduojami. Tada „React“ perima valdymą, efektyviai atnaujindamas realų naršyklės DOM, kad atspindėtų jūsų deklaruotą vartotojo sąsają. Šis abstrakcijos lygmuo yra nepaprastai galingas, apsaugantis programuotojus nuo tiesioginės DOM manipuliacijos sudėtingumo ir našumo spąstų. Būtent dėl to „React“ programos dažnai veikia taip sklandžiai ir greitai.
Vienakryptis duomenų srautas ir jo ribos
„React“ architektūrinė stiprybė slypi vienakrypčiame duomenų sraute. Duomenys nuspėjamai teka žemyn iš tėvinių komponentų į vaikinius per props, o būsenos pokyčiai komponente sukelia perpiešimus, kurie sklinda per jo medžio struktūrą. Šis modelis skatina nuspėjamumą ir žymiai palengvina derinimą, nes visada žinote, iš kur atsiranda duomenys ir kaip jie veikia vartotojo sąsają. Tačiau ne visos sąveikos puikiai dera su šiuo iš viršaus į apačią nukreiptu duomenų srautu.
Apsvarstykite tokius scenarijus kaip:
- Programiškai fokusuoti įvesties lauką, kai vartotojas pereina į formą.
- Paleisti
play()arbapause()metodus<video>elemente. - Išmatuoti tikslius atvaizduoto
<div>pikselių matmenis, norint dinamiškai koreguoti išdėstymą. - Integruoti sudėtingą trečiosios šalies JavaScript biblioteką (pvz., grafikų biblioteką, kaip D3.js, ar žemėlapių vizualizavimo įrankį), kuri tikisi tiesioginės prieigos prie DOM konteinerio.
Šie veiksmai iš prigimties yra imperatyvūs – jie apima tiesioginį komandavimą elementui kažką daryti, o ne tik jo norimos būsenos deklaravimą. Nors „React“ deklaratyvus modelis dažnai gali abstrahuoti daugybę imperatyvių detalių, jis visiškai nepanaikina jų poreikio. Būtent čia į pagalbą ateina „refs“, suteikdami kontroliuojamą avarinį išėjimą šioms tiesioginėms sąveikoms atlikti.
Kada naudoti „Refs“: Imperatyvių ir deklaratyvių sąveikų naršymas
Svarbiausias principas dirbant su „refs“ yra juos naudoti saikingai ir tik tada, kai tai absoliučiai būtina. Jei užduotį galima atlikti naudojant standartinius „React“ deklaratyvius mechanizmus (būseną ir props), tai visada turėtų būti jūsų pageidaujamas metodas. Pernelyg didelis pasikliovimas „refs“ gali lemti kodą, kurį sunkiau suprasti, prižiūrėti ir derinti, taip sumenkinant pačios „React“ teikiamus privalumus.
Tačiau situacijose, kurios iš tiesų reikalauja tiesioginės prieigos prie DOM mazgo ar komponento egzemplioriaus, „refs“ yra teisingas ir numatytas sprendimas. Štai išsamesnis tinkamų naudojimo atvejų sąrašas:
- Fokusavimo, teksto žymėjimo ir medijos atkūrimo valdymas: Tai klasikiniai pavyzdžiai, kai reikia imperatyviai sąveikauti su elementais. Pagalvokite apie automatinį paieškos lauko fokusavimą įkėlus puslapį, viso teksto pažymėjimą įvesties lauke arba garso ar vaizdo grotuvo atkūrimo valdymą. Šiuos veiksmus paprastai sukelia vartotojo įvykiai ar komponento gyvavimo ciklo metodai, o ne tiesiog props ar būsenos keitimas.
- Imperatyvių animacijų paleidimas: Nors daugelį animacijų galima valdyti deklaratyviai su CSS perėjimais/animacijomis ar „React“ animacijos bibliotekomis, kai kurios sudėtingos, didelio našumo animacijos, ypač tos, kurios naudoja HTML Canvas API, WebGL, ar reikalauja smulkmeniško elementų savybių valdymo, kurias geriausia valdyti už „React“ atvaizdavimo ciklo ribų, gali pareikalauti „refs“.
- Integracija su trečiųjų šalių DOM bibliotekomis: Daugelis senų ir gerbiamų JavaScript bibliotekų (pvz., D3.js, Leaflet žemėlapiams, įvairūs senesni UI įrankių rinkiniai) yra sukurtos tiesiogiai manipuliuoti konkrečiais DOM elementais. „Refs“ suteikia esminį tiltą, leidžiantį „React“ atvaizduoti konteinerio elementą, o tada suteikti trečiosios šalies bibliotekai prieigą prie to konteinerio jos pačios imperatyviai atvaizdavimo logikai.
-
Elemento matmenų ar padėties matavimas: Norint įgyvendinti pažangius išdėstymus, virtualizaciją ar pasirinktinį slinkimo elgesį, dažnai reikia tikslios informacijos apie elemento dydį, jo padėtį peržiūros srities atžvilgiu ar jo slinkimo aukštį. Tokios API kaip
getBoundingClientRect()yra prieinamos tik realiuose DOM mazguose, todėl „refs“ yra būtini tokiems skaičiavimams.
Ir atvirkščiai, turėtumėte vengti naudoti „refs“ užduotims, kurias galima atlikti deklaratyviai. Tai apima:
- Komponento stiliaus keitimą (naudokite būseną sąlyginiam stiliavimui).
- Elemento teksto turinio keitimą (perduokite kaip prop arba atnaujinkite būseną).
- Sudėtingą komponentų komunikaciją (props ir atgalinio ryšio funkcijos (callbacks) paprastai yra geresnis pasirinkimas).
- Bet kokį scenarijų, kai bandote atkartoti būsenos valdymo funkcionalumą.
Giluminis žvilgsnis į React.createRef(): Modernus požiūris klasės komponentams
React.createRef() buvo pristatytas „React“ 16.3 versijoje, suteikdamas aiškesnį ir švaresnį būdą valdyti „refs“, palyginti su senesniais metodais, tokiais kaip eilutės tipo „refs“ (dabar pasenę) ir atgalinio ryšio „refs“ (vis dar galiojantys, bet dažnai sudėtingesni). Jis sukurtas kaip pagrindinis „ref“ kūrimo mechanizmas klasės komponentams, siūlantis objektiniu programavimu pagrįstą API, kuri natūraliai dera su klasės struktūra.
Sintaksė ir pagrindinis naudojimas: Trijų žingsnių procesas
Darbo eiga naudojant createRef() yra paprasta ir apima tris pagrindinius žingsnius:
-
Sukurti Ref objektą: Savo klasės komponento konstruktoriuje inicializuokite „ref“ egzempliorių iškviesdami
React.createRef()ir priskirdami jo grąžinamą reikšmę egzemplioriaus savybei (pvz.,this.myRef). -
Priskirti Ref: Savo komponento
rendermetode perduokite sukurtą „ref“ objektąrefatributui „React“ elemento (HTML elemento arba klasės komponento), kurį norite nurodyti. -
Pasiekti tikslą: Kai komponentas bus prijungtas (mounted), nurodytas DOM mazgas ar komponento egzempliorius bus pasiekiamas per jūsų „ref“ objekto
.currentsavybę (pvz.,this.myRef.current).
import React from 'react';
class FocusInputOnMount extends React.Component {
constructor(props) {
super(props);
this.inputElementRef = React.createRef(); // 1 žingsnis: Konstruktoriuje sukuriame ref objektą
console.log('Constructor: Ref current value is initially:', this.inputElementRef.current); // null
}
componentDidMount() {
if (this.inputElementRef.current) {
this.inputElementRef.current.focus();
console.log('ComponentDidMount: Input focused. Current value:', this.inputElementRef.current.value);
}
}
handleButtonClick = () => {
if (this.inputElementRef.current) {
alert(`Input value: ${this.inputElementRef.current.value}`);
}
};
render() {
console.log('Render: Ref current value is:', this.inputElementRef.current); // Pradinio atvaizdavimo metu vis dar null
return (
<div style={{ padding: '20px', border: '1px solid #ccc', borderRadius: '8px' }}>
<h3>Automatiškai fokusuojamas įvesties laukas</h3>
<label htmlFor="focusInput">Įveskite savo vardą:</label><br />
<input
id="focusInput"
type="text"
ref={this.inputElementRef} // 2 žingsnis: Priskiriame ref <input> elementui
placeholder="Jūsų vardas..."
style={{ margin: '10px 0', padding: '8px', borderRadius: '4px', border: '1px solid #ddd' }}
/><br />
<button
onClick={this.handleButtonClick}
style={{ padding: '10px 15px', background: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Rodyti įvesties reikšmę
</button>
<p><em>Šis įvesties laukas bus automatiškai fokusuotas, kai komponentas įsikels.</em></p>
</div>
);
}
}
Šiame pavyzdyje this.inputElementRef yra objektas, kurį „React“ valdo viduje. Kai <input> elementas atvaizduojamas ir prijungiamas prie DOM, „React“ priskiria tą realų DOM mazgą this.inputElementRef.current savybei. componentDidMount gyvavimo ciklo metodas yra ideali vieta sąveikauti su „refs“, nes jis garantuoja, kad komponentas ir jo vaikiniai elementai buvo atvaizduoti DOM ir kad .current savybė yra prieinama ir užpildyta.
Ref prijungimas prie DOM elemento: Tiesioginė prieiga prie DOM
Kai prijungiate „ref“ prie standartinio HTML elemento (pvz., <div>, <p>, <button>, <img>), jūsų „ref“ objekto .current savybė laikys realų pagrindinį DOM elementą. Tai suteikia jums neribotą prieigą prie visų standartinių naršyklės DOM API, leidžiančių atlikti veiksmus, kurie paprastai yra už „React“ deklaratyvios kontrolės ribų. Tai ypač naudinga globalioms programoms, kur tikslus išdėstymas, slinkimas ar fokusavimo valdymas gali būti kritiškai svarbūs įvairiose vartotojų aplinkose ir įrenginių tipuose.
import React from 'react';
class ScrollToElementExample extends React.Component {
constructor(props) {
super(props);
this.targetDivRef = React.createRef();
this.state = { showScrollButton: false };
}
componentDidMount() {
// Rodyti slinkimo mygtuką tik jei yra pakankamai turinio slinkti
// Šis patikrinimas taip pat užtikrina, kad ref jau yra priskirtas.
if (this.targetDivRef.current && window.innerHeight < document.body.scrollHeight) {
this.setState({ showScrollButton: true });
}
}
handleScrollToTarget = () => {
if (this.targetDivRef.current) {
// Naudojame scrollIntoView sklandžiam slinkimui, plačiai palaikomam visose naršyklėse.
this.targetDivRef.current.scrollIntoView({
behavior: 'smooth', // Animuojamas slinkimas geresnei vartotojo patirčiai
block: 'start' // Sulygiuoja elemento viršų su peržiūros srities viršumi
});
console.log('Scrolled to target div!');
} else {
console.warn('Target div not yet available for scrolling.');
}
};
render() {
return (
<div style={{ padding: '15px' }}>
<h2>Slinkimas prie konkretaus elemento su Ref</h2>
<p>Šis pavyzdys demonstruoja, kaip programiškai nuslinkti prie DOM elemento, kuris yra už ekrano ribų.</p>
{this.state.showScrollButton && (
<button
onClick={this.handleScrollToTarget}
style={{ marginBottom: '20px', padding: '10px 20px', background: '#28a745', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Slinkti žemyn į tikslinę sritį
</button>
)}
<div style={{ height: '1500px', background: '#f8f9fa', padding: '20px', marginBottom: '20px', border: '1px dashed #6c757d' }}>
<p>Užpildo turinys, skirtas sukurti vertikalios slinkties erdvę.</p>
<p>Įsivaizduokite ilgus straipsnius, sudėtingas formas ar detalias prietaisų skydelius, reikalaujančius, kad vartotojai naršytų po didelį turinio kiekį. Programinis slinkimas užtikrina, kad vartotojai galėtų greitai pasiekti svarbias dalis be rankinių pastangų, gerinant prieinamumą ir vartotojo srautą visuose įrenginiuose ir ekranų dydžiuose.</p>
<p>Ši technika ypač naudinga daugiapuslapėse formose, žingsnis po žingsnio vedliuose ar vieno puslapio programose su gilia navigacija.</p>
</div>
<div
ref={this.targetDivRef} // Čia priskiriame ref
style={{
minHeight: '300px',
background: '#e9ecef',
padding: '30px',
border: '2px solid #007bff',
borderRadius: '10px',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
textAlign: 'center'
}}
>
<h3>Jūs pasiekėte tikslinę sritį!</h3>
<p>Tai yra sritis, prie kurios nuslinkome programiškai.</p>
<p>Galimybė tiksliai valdyti slinkimo elgesį yra labai svarbi gerinant vartotojo patirtį, ypač mobiliuosiuose įrenginiuose, kur ekrano erdvė yra ribota, o tiksli navigacija yra svarbiausia.</p>
</div>
</div>
);
}
}
Šis pavyzdys puikiai iliustruoja, kaip createRef suteikia kontrolę naršyklės lygio sąveikoms. Tokios programinio slinkimo galimybės yra kritiškai svarbios daugelyje programų, nuo ilgos dokumentacijos naršymo iki vartotojų vedimo per sudėtingas darbo eigas. behavior: 'smooth' parinktis scrollIntoView metode užtikrina malonų, animuotą perėjimą, visuotinai gerinantį vartotojo patirtį.
Ref prijungimas prie klasės komponento: Sąveika su egzemplioriais
Be vietinių DOM elementų, „ref“ taip pat galite prijungti prie klasės komponento egzemplioriaus. Kai tai padarote, jūsų „ref“ objekto .current savybė laikys patį realų suinstancijuotą klasės komponentą. Tai leidžia tėviniam komponentui tiesiogiai kviesti vaikinio klasės komponente apibrėžtus metodus arba pasiekti jo egzemplioriaus savybes. Nors tai galinga galimybė, ją reikėtų naudoti itin atsargiai, nes ji leidžia pažeisti tradicinį vienakryptį duomenų srautą, kas gali lemti mažiau nuspėjamą programos elgesį.
import React from 'react';
// Vaikinis klasės komponentas
class DialogBox extends React.Component {
constructor(props) {
super(props);
this.state = { isOpen: false, message: '' };
}
// Metodas, prieinamas tėviniam komponentui per ref
open(message) {
this.setState({ isOpen: true, message });
}
close = () => {
this.setState({ isOpen: false, message: '' });
};
render() {
if (!this.state.isOpen) return null;
return (
<div style={{
position: 'fixed', top: '50%', left: '50%', transform: 'translate(-50%, -50%)',
padding: '25px 35px', background: 'white', border: '1px solid #ddd', borderRadius: '8px',
boxShadow: '0 5px 15px rgba(0,0,0,0.2)', zIndex: 1000, maxWidth: '400px', width: '90%', textAlign: 'center'
}}>
<h4>Žinutė iš tėvinio komponento</h4>
<p>{this.state.message}</p>
<button
onClick={this.close}
style={{ marginTop: '15px', padding: '8px 15px', background: '#dc3545', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}
>
Uždaryti
</button>
</div>
);
}
}
// Tėvinis klasės komponentas
class AppWithDialog extends React.Component {
constructor(props) {
super(props);
this.dialogRef = React.createRef();
}
handleOpenDialog = () => {
if (this.dialogRef.current) {
// Pasiekiame vaikinio komponento egzempliorių ir iškviečiame jo 'open' metodą
this.dialogRef.current.open('Sveiki iš tėvinio komponento! Šis dialogas buvo atidarytas imperatyviai.');
}
};
render() {
return (
<div style={{ padding: '20px', textAlign: 'center' }}>
<h2>Tėvinio ir vaikinio komponentų komunikacija per Ref</h2>
<p>Tai demonstruoja, kaip tėvinis komponentas gali imperatyviai valdyti savo vaikinio klasės komponento metodą.</p>
<button
onClick={this.handleOpenDialog}
style={{ padding: '12px 25px', background: '#007bff', color: 'white', border: 'none', borderRadius: '6px', cursor: 'pointer', fontSize: '1.1em' }}
>
Atidaryti imperatyvų dialogą
</button>
<DialogBox ref={this.dialogRef} /> // Priskiriame ref klasės komponento egzemplioriui
</div>
);
}
}
Čia AppWithDialog gali tiesiogiai iškviesti DialogBox komponento open metodą per jo „ref“. Šis modelis gali būti naudingas norint paleisti veiksmus, tokius kaip modalo rodymas, formos atstatymas ar programiškai valdyti išorinius UI elementus, esančius vaikinio komponente. Tačiau dažniausiai rekomenduojama teikti pirmenybę prop'ais pagrįstai komunikacijai, perduodant duomenis ir atgalinio ryšio funkcijas iš tėvinio į vaikinį komponentą, kad būtų palaikomas aiškus ir nuspėjamas duomenų srautas. Prie „refs“ vaikinio komponento metodams kreipkitės tik tada, kai tie veiksmai yra iš tiesų imperatyvūs ir neatitinka tipiško prop/būsenos srauto.
Ref prijungimas prie funkcinio komponento (Esminis skirtumas)
Dažnai pasitaikantis klaidingas įsitikinimas ir svarbus skirtumas yra tai, kad negalima tiesiogiai prijungti „ref“ naudojant createRef() prie funkcinio komponento. Funkciniai komponentai iš prigimties neturi egzempliorių taip, kaip klasės komponentai. Jei bandysite priskirti „ref“ tiesiogiai funkciniam komponentui (pvz., <MyFunctionalComponent ref={this.myRef} />), „React“ kūrimo režimu pateiks įspėjimą, nes nėra komponento egzemplioriaus, kurį būtų galima priskirti .current savybei.
Jei jūsų tikslas yra leisti tėviniam komponentui (kuris gali būti klasės komponentas, naudojantis createRef, arba funkcinis komponentas, naudojantis useRef) pasiekti DOM elementą, atvaizduotą viduje funkcinio vaikinio komponento, turite naudoti React.forwardRef. Šis aukštesnės eilės komponentas leidžia funkciniams komponentams atverti „ref“ prieigą prie konkretaus DOM mazgo arba imperatyvios rankenėlės (handle) savyje.
Alternatyviai, jei dirbate viduje funkcinio komponento ir jums reikia sukurti bei valdyti „ref“, tinkamas mechanizmas yra useRef kabliukas (hook), kuris bus trumpai aptartas vėlesniame palyginimo skyriuje. Svarbu prisiminti, kad createRef iš esmės yra susijęs su klasės komponentais ir jų egzemplioriais pagrįsta prigimtimi.
Prieiga prie DOM mazgo ar komponento egzemplioriaus: .current savybės paaiškinimas
„Ref“ sąveikos esmė sukasi apie .current savybę, esančią React.createRef() sukurtame „ref“ objekte. Jos gyvavimo ciklo ir to, ką ji gali laikyti, supratimas yra svarbiausias efektyviam „ref“ valdymui.
.current savybė: Jūsų vartai į imperatyvų valdymą
.current savybė yra kintamas objektas, kurį valdo „React“. Ji tarnauja kaip tiesioginis ryšys su nurodytu elementu ar komponento egzemplioriumi. Jos reikšmė kinta per visą komponento gyvavimo ciklą:
-
Inicializacija: Kai pirmą kartą iškviečiate
React.createRef()konstruktoriuje, sukuriamas „ref“ objektas, o jo.currentsavybė inicializuojama kaipnull. Taip yra todėl, kad šiame etape komponentas dar nebuvo atvaizduotas, ir nėra DOM elemento ar komponento egzemplioriaus, į kurį „ref“ galėtų rodyti. -
Prijungimas (Mounting): Kai komponentas atvaizduojamas DOM ir sukuriamas elementas su
refatributu, „React“ priskiria realų DOM mazgą arba klasės komponento egzempliorių jūsų „ref“ objekto.currentsavybei. Tai paprastai įvyksta iškart porendermetodo pabaigos ir prieš iškviečiantcomponentDidMount. TodėlcomponentDidMountyra saugiausia ir dažniausiai naudojama vieta pasiekti ir sąveikauti su.current. -
Atjungimas (Unmounting): Kai komponentas atjungiamas nuo DOM, „React“ automatiškai atstato
.currentsavybę atgal įnull. Tai yra labai svarbu siekiant išvengti atminties nutekėjimų ir užtikrinti, kad jūsų programa nelaikytų nuorodų į elementus, kurie nebėra DOM. -
Atnaujinimas (Updating): Retais atvejais, kai
refatributas pakeičiamas elemente atnaujinimo metu, senojo „ref“currentsavybė bus nustatyta įnullprieš nustatant naujojo „ref“currentsavybę. Šis elgesys yra retesnis, bet svarbus atkreipti dėmesį esant sudėtingiems dinaminiams „ref“ priskyrimams.
import React from 'react';
class RefLifecycleLogger extends React.Component {
constructor(props) {
super(props);
this.myDivRef = React.createRef();
console.log('1. Constructor: this.myDivRef.current is', this.myDivRef.current); // null
}
componentDidMount() {
console.log('3. componentDidMount: this.myDivRef.current is', this.myDivRef.current); // Realus DOM elementas
if (this.myDivRef.current) {
this.myDivRef.current.style.backgroundColor = '#d4edda'; // Imperatyvus stiliavimas demonstracijai
this.myDivRef.current.innerText += ' - Ref yra aktyvus!';
}
}
componentDidUpdate(prevProps, prevState) {
console.log('4. componentDidUpdate: this.myDivRef.current is', this.myDivRef.current); // Realus DOM elementas (po atnaujinimų)
}
componentWillUnmount() {
console.log('5. componentWillUnmount: this.myDivRef.current is', this.myDivRef.current); // Realus DOM elementas (prieš pat nustatant į null)
// Šiame etape, jei reikia, galite atlikti valymo darbus
}
render() {
// Pirminio atvaizdavimo metu this.myDivRef.current vis dar yra null, nes DOM dar nebuvo sukurtas.
// Vėlesniuose atvaizdavimuose (po prijungimo) jis laikys elementą.
console.log('2. Render: this.myDivRef.current is', this.myDivRef.current);
return (
<div
ref={this.myDivRef}
style={{ padding: '20px', border: '1px solid #28a745', margin: '20px', minHeight: '80px', display: 'flex', alignItems: 'center' }}
>
<p>Tai yra div elementas, prie kurio prijungtas ref.</p>
</div>
);
}
}
Stebint konsolės išvestį RefLifecycleLogger komponentui, aiškiai matyti, kada this.myDivRef.current tampa prieinamas. Labai svarbu visada patikrinti, ar this.myDivRef.current nėra null, prieš bandant su juo sąveikauti, ypač metoduose, kurie gali būti paleisti prieš prijungimą arba po atjungimo.
Ką gali laikyti `.current`? Jūsų „Ref“ turinio tyrinėjimas
Reikšmės tipas, kurį laiko current, priklauso nuo to, prie ko prijungiate „ref“:
-
Kai prijungta prie HTML elemento (pvz.,
<div>,<input>):.currentsavybė laikys realų pagrindinį DOM elementą. Tai yra vietinis JavaScript objektas, suteikiantis prieigą prie viso jo DOM API spektro. Pavyzdžiui, jei prijungsite „ref“ prie<input type="text">,.currentbusHTMLInputElementobjektas, leidžiantis jums kviesti metodus, tokius kaip.focus(), skaityti savybes, tokias kaip.value, ar keisti atributus, tokius kaip.placeholder. Tai yra dažniausias „refs“ naudojimo atvejis.this.inputRef.current.focus();
this.videoRef.current.play();
const { width, height } = this.divRef.current.getBoundingClientRect(); -
Kai prijungta prie klasės komponento (pvz.,
<MyClassComponent />):.currentsavybė laikys to klasės komponento egzempliorių. Tai reiškia, kad galite tiesiogiai kviesti metodus, apibrėžtus tame vaikiniame komponente (pvz.,childRef.current.someMethod()) ar net pasiekti jo būseną ar props (nors tiesioginis būsenos/props pasiekimas iš vaikinio komponento per „ref“ paprastai nerekomenduojamas, pirmenybę teikiant props ir būsenos atnaujinimams). Ši galimybė yra galinga norint sužadinti specifinį elgesį vaikinio komponentuose, kuris netelpa į standartinį prop'ais pagrįstą sąveikos modelį.this.childComponentRef.current.resetForm();
// Retai, bet įmanoma: console.log(this.childComponentRef.current.state.someValue); -
Kai prijungta prie funkcinio komponento (per
forwardRef): Kaip minėta anksčiau, „refs“ negalima tiesiogiai prijungti prie funkcinių komponentų. Tačiau, jei funkcinis komponentas yra apgaubtasReact.forwardRef, tada.currentsavybė laikys bet kokią reikšmę, kurią funkcinis komponentas aiškiai atveria per perduotą „ref“. Tai paprastai yra DOM elementas funkciniame komponente arba objektas su imperatyviais metodais (naudojantuseImperativeHandlekabliuką kartu suforwardRef).// Tėviniame komponente myForwardedRef.current būtų atvertas DOM mazgas arba objektas
this.myForwardedRef.current.focus();
this.myForwardedRef.current.customResetMethod();
Praktiniai `createRef` panaudojimo pavyzdžiai
Norint iš tiesų suprasti React.createRef() naudingumą, panagrinėkime išsamesnius, globaliai aktualius scenarijus, kur jis pasirodo esąs nepakeičiamas, peržengiant paprastą fokusavimo valdymą.
1. Fokusavimo, teksto žymėjimo ar medijos atkūrimo valdymas įvairiose kultūrose
Tai yra pagrindiniai imperatyvių UI sąveikų pavyzdžiai. Įsivaizduokite daugiapakopę formą, skirtą globaliai auditorijai. Po to, kai vartotojas užpildo vieną skyrių, galbūt norėsite automatiškai perkelti fokusą į kito skyriaus pirmąjį įvesties lauką, nepriklausomai nuo kalbos ar numatytosios teksto krypties (iš kairės į dešinę arba iš dešinės į kairę). „Refs“ suteikia būtiną kontrolę.
import React from 'react';
class DynamicFocusForm extends React.Component {
constructor(props) {
super(props);
this.firstNameRef = React.createRef();
this.lastNameRef = React.createRef();
this.emailRef = React.createRef();
this.state = { currentStep: 1 };
}
componentDidMount() {
// Fokusuoti pirmą įvesties lauką, kai komponentas prijungiamas
this.firstNameRef.current.focus();
}
handleNextStep = (nextRef) => {
this.setState(prevState => ({ currentStep: prevState.currentStep + 1 }), () => {
// Po būsenos atnaujinimo ir komponento perpiešimo, fokusuoti kitą įvesties lauką
if (nextRef.current) {
nextRef.current.focus();
}
});
};
render() {
const { currentStep } = this.state;
const formSectionStyle = { border: '1px solid #0056b3', padding: '20px', margin: '15px 0', borderRadius: '8px', background: '#e7f0fa' };
const inputStyle = { width: '100%', padding: '10px', margin: '8px 0', border: '1px solid #ccc', borderRadius: '4px' };
const buttonStyle = { padding: '10px 20px', background: '#007bff', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', marginTop: '10px' };
return (
<div style={{ maxWidth: '600px', margin: '30px auto', padding: '25px', boxShadow: '0 4px 12px rgba(0,0,0,0.1)', borderRadius: '10px', background: 'white' }}>
<h2>Daugiapakopė forma su Ref valdomu fokusavimu</h2>
<p>Dabartinis žingsnis: <strong>{currentStep}</strong></p>
{currentStep === 1 && (
<div style={formSectionStyle}>
<h3>Asmeniniai duomenys</h3>
<label htmlFor="firstName">Vardas:</label>
<input id="firstName" type="text" ref={this.firstNameRef} style={inputStyle} placeholder="pvz., Jonas" />
<label htmlFor="lastName">Pavardė:</label>
<input id="lastName" type="text" ref={this.lastNameRef} style={inputStyle} placeholder="pvz., Jonaitis" />
<button onClick={() => this.handleNextStep(this.emailRef)} style={buttonStyle}>Kitas →</button>
</div>
)}
{currentStep === 2 && (
<div style={formSectionStyle}>
<h3>Kontaktinė informacija</h3>
<label htmlFor="email">El. paštas:</label>
<input id="email" type="email" ref={this.emailRef} style={inputStyle} placeholder="pvz., jonas.jonaitis@example.com" />
<p>... kiti kontaktiniai laukai ...</p>
<button onClick={() => alert('Forma pateikta!')} style={buttonStyle}>Pateikti</button>
</div>
)}
<p><em>Ši sąveika žymiai pagerina prieinamumą ir vartotojo patirtį, ypač vartotojams, besinaudojantiems klaviatūros navigacija ar pagalbinėmis technologijomis visame pasaulyje.</em></p>
</div>
);
}
}
Šis pavyzdys demonstruoja praktišką daugiapakopę formą, kurioje createRef naudojamas programiškai valdyti fokusavimą. Tai užtikrina sklandžią ir prieinamą vartotojo kelionę, kas yra kritiškai svarbu programoms, naudojamoms įvairiuose lingvistiniuose ir kultūriniuose kontekstuose. Panašiai, medijos grotuvams, „refs“ leidžia sukurti pasirinktinius valdiklius (groti, pauzė, garsumas, persukimas), kurie tiesiogiai sąveikauja su HTML5 <video> ar <audio> elementų vietinėmis API, suteikiant nuoseklią patirtį, nepriklausomai nuo naršyklės numatytųjų nustatymų.
2. Imperatyvių animacijų ir Canvas sąveikų paleidimas
Nors deklaratyvios animacijų bibliotekos puikiai tinka daugeliui UI efektų, kai kurios pažangios animacijos, ypač tos, kurios naudoja HTML5 Canvas API, WebGL, ar reikalauja smulkmeniško elementų savybių valdymo, kurias geriausia valdyti už „React“ atvaizdavimo ciklo ribų, labai laimi iš „refs“ naudojimo. Pavyzdžiui, realaus laiko duomenų vizualizacijos ar žaidimo kūrimas Canvas elemente apima tiesioginį piešimą ant pikselių buferio, kas yra iš prigimties imperatyvus procesas.
import React from 'react';
class CanvasAnimator extends React.Component {
constructor(props) {
super(props);
this.canvasRef = React.createRef();
this.animationFrameId = null;
}
componentDidMount() {
this.startAnimation();
}
componentWillUnmount() {
this.stopAnimation();
}
startAnimation = () => {
const canvas = this.canvasRef.current;
if (!canvas) return;
const ctx = canvas.getContext('2d');
let angle = 0;
const centerX = canvas.width / 2;
const centerY = canvas.height / 2;
const radius = 50;
const animate = () => {
ctx.clearRect(0, 0, canvas.width, canvas.height); // Išvalyti drobę
// Nupiešti besisukantį kvadratą
ctx.save();
ctx.translate(centerX, centerY);
ctx.rotate(angle);
ctx.fillStyle = '#6f42c1';
ctx.fillRect(-radius / 2, -radius / 2, radius, radius);
ctx.restore();
angle += 0.05; // Padidinti kampą sukimosi efektui
this.animationFrameId = requestAnimationFrame(animate);
};
this.animationFrameId = requestAnimationFrame(animate);
};
stopAnimation = () => {
if (this.animationFrameId) {
cancelAnimationFrame(this.animationFrameId);
}
};
render() {
return (
<div style={{ textAlign: 'center', margin: '30px auto', border: '1px solid #ced4da', padding: '20px', borderRadius: '8px', background: '#f8f9fa' }}>
<h3>Imperatyvi Canvas animacija su createRef</h3>
<p>Ši drobės animacija valdoma tiesiogiai naudojant naršyklės API per ref.</p>
<canvas ref={this.canvasRef} width="300" height="200" style={{ border: '1px solid #adb5bd', background: 'white' }}>
Jūsų naršyklė nepalaiko HTML5 canvas elemento.
</canvas>
<p><em>Tokia tiesioginė kontrolė yra gyvybiškai svarbi didelio našumo grafikams, žaidimams ar specializuotoms duomenų vizualizacijoms, naudojamoms įvairiose pramonės šakose visame pasaulyje.</em></p>
</div>
);
}
}
Šis komponentas suteikia drobės elementą ir naudoja „ref“, kad gautų tiesioginę prieigą prie jo 2D atvaizdavimo konteksto. Animacijos ciklas, varomas `requestAnimationFrame`, tada imperatyviai piešia ir atnaujina besisukantį kvadratą. Šis modelis yra fundamentalus kuriant interaktyvias duomenų prietaisų skydelius, internetinius dizaino įrankius ar net paprastus žaidimus, kurie reikalauja tikslaus, kadras po kadro atvaizdavimo, nepriklausomai nuo vartotojo geografinės vietos ar įrenginio galimybių.
3. Integracija su trečiųjų šalių DOM bibliotekomis: Sklandus tiltas
Viena iš įtikinamiausių priežasčių naudoti „refs“ yra integruoti „React“ su išorinėmis JavaScript bibliotekomis, kurios tiesiogiai manipuliuoja DOM. Daugelis galingų bibliotekų, ypač senesnės arba skirtos specifinėms atvaizdavimo užduotims (pvz., diagramų kūrimui, žemėlapiams, teksto redagavimui), veikia paimdamos DOM elementą kaip tikslą ir pačios valdydamos jo turinį. „React“ savo deklaratyviu režimu kitu atveju konfliktuotų su šiomis bibliotekomis, bandydamas valdyti tą patį DOM pomedį. „Refs“ užkerta kelią šiam konfliktui, suteikdami paskirtą „konteinerį“ išorinei bibliotekai.
import React from 'react';
import * as d3 from 'd3'; // Darant prielaidą, kad D3.js yra įdiegta ir importuota
class D3BarChart extends React.Component {
constructor(props) {
super(props);
this.chartContainerRef = React.createRef();
}
// Kai komponentas prijungiamas, nupiešiame diagramą
componentDidMount() {
this.drawChart();
}
// Kai komponentas atsinaujina (pvz., keičiasi props.data), atnaujiname diagramą
componentDidUpdate(prevProps) {
if (prevProps.data !== this.props.data) {
this.drawChart();
}
}
// Kai komponentas atjungiamas, išvalome D3 elementus, kad išvengtume atminties nutekėjimo
componentWillUnmount() {
d3.select(this.chartContainerRef.current).selectAll('*').remove();
}
drawChart = () => {
const data = this.props.data || [40, 80, 20, 100, 60, 90]; // Numatytieji duomenys
const node = this.chartContainerRef.current;
if (!node) return; // Užtikriname, kad ref yra prieinamas
// Išvalome visus ankstesnius D3 nupieštus diagramos elementus
d3.select(node).selectAll('*').remove();
const margin = { top: 20, right: 20, bottom: 30, left: 40 };
const width = 460 - margin.left - margin.right;
const height = 300 - margin.top - margin.bottom;
const svg = d3.select(node)
.append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
// Nustatome skales
const x = d3.scaleBand()
.range([0, width])
.padding(0.1);
const y = d3.scaleLinear()
.range([height, 0]);
x.domain(data.map((d, i) => i)); // Paprastumo dėlei naudojame indeksą kaip domeną
y.domain([0, d3.max(data)]);
// Pridedame stulpelius
svg.selectAll('.bar')
.data(data)
.enter().append('rect')
.attr('class', 'bar')
.attr('x', (d, i) => x(i))
.attr('width', x.bandwidth())
.attr('y', d => y(d))
.attr('height', d => height - y(d))
.attr('fill', '#17a2b8');
// Pridedame X ašį
svg.append('g')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x));
// Pridedame Y ašį
svg.append('g')
.call(d3.axisLeft(y));
};
render() {
return (
<div style={{ textAlign: 'center', margin: '30px auto', border: '1px solid #00a0b2', padding: '20px', borderRadius: '8px', background: '#e0f7fa' }}>
<h3>D3.js diagramos integracija su React createRef</h3>
<p>Ši duomenų vizualizacija atvaizduojama D3.js biblioteka React valdomame konteineryje.</p>
<div ref={this.chartContainerRef} /> // D3.js atvaizduos į šį div
<p><em>Tokių specializuotų bibliotekų integravimas yra labai svarbus daug duomenų turinčioms programoms, suteikiant galingus analizės įrankius vartotojams įvairiose pramonės šakose ir regionuose.</em></p>
</div>
);
}
}
Šis išsamus pavyzdys demonstruoja D3.js stulpelinės diagramos integraciją į „React“ klasės komponentą. chartContainerRef suteikia D3.js konkretų DOM mazgą, kurio jam reikia atvaizdavimui atlikti. „React“ valdo konteinerio <div> gyvavimo ciklą, o D3.js valdo jo vidinį turinį. `componentDidUpdate` ir `componentWillUnmount` metodai yra gyvybiškai svarbūs atnaujinant diagramą pasikeitus duomenims ir atliekant būtiną valymą, užkertant kelią atminties nutekėjimams ir užtikrinant greitą veikimą. Šis modelis yra universalus, leidžiantis programuotojams pasinaudoti geriausiais abiejų – „React“ komponentų modelio ir specializuotų, didelio našumo vizualizacijos bibliotekų – aspektais, kuriant globalias prietaisų skydelius ir analitikos platformas.
4. Elementų matmenų ar padėties matavimas dinamiškiems išdėstymams
Labai dinamiškiems ar prisitaikantiems išdėstymams, arba įgyvendinant funkcijas, tokias kaip virtualizuoti sąrašai, kurie atvaizduoja tik matomus elementus, labai svarbu žinoti tikslius elementų matmenis ir padėtį. „Refs“ leidžia pasiekti getBoundingClientRect() metodą, kuris suteikia šią svarbią informaciją tiesiogiai iš DOM.
import React from 'react';
class ElementDimensionLogger extends React.Component {
constructor(props) {
super(props);
this.measurableDivRef = React.createRef();
this.state = {
width: 0,
height: 0,
top: 0,
left: 0,
message: 'Spustelėkite mygtuką, kad išmatuotumėte!'
};
}
componentDidMount() {
// Pradinis matavimas dažnai naudingas, bet gali būti paleistas ir vartotojo veiksmu
this.measureElement();
// Dinamiškiems išdėstymams galite klausytis lango dydžio keitimo įvykių
window.addEventListener('resize', this.measureElement);
}
componentWillUnmount() {
window.removeEventListener('resize', this.measureElement);
}
measureElement = () => {
if (this.measurableDivRef.current) {
const rect = this.measurableDivRef.current.getBoundingClientRect();
this.setState({
width: Math.round(rect.width),
height: Math.round(rect.height),
top: Math.round(rect.top),
left: Math.round(rect.left),
message: 'Matmenys atnaujinti.'
});
} else {
this.setState({ message: 'Elementas dar neatvaizduotas.' });
}
};
render() {
const { width, height, top, left, message } = this.state;
const boxStyle = {
width: '70%',
minHeight: '150px',
border: '3px solid #ffc107',
margin: '25px auto',
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
background: '#fff3cd',
borderRadius: '8px',
textAlign: 'center'
};
return (
<div style={{ maxWidth: '700px', margin: '30px auto', padding: '25px', boxShadow: '0 4px 12px rgba(0,0,0,0.08)', borderRadius: '10px', background: 'white' }}>
<h3>Elemento matmenų matavimas su createRef</h3>
<p>Šis pavyzdys dinamiškai gauna ir rodo tikslinio elemento dydį ir padėtį.</p>
<div ref={this.measurableDivRef} style={boxStyle}>
<p><strong>Aš esu matuojamas elementas.</strong></p>
<p>Pakeiskite naršyklės lango dydį, kad pamatytumėte, kaip keičiasi matmenys atnaujinant/rankiniu būdu.</p>
</div>
<button
onClick={this.measureElement}
style={{ padding: '10px 20px', background: '#6c757d', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer', marginBottom: '15px' }}
>
Matuoti dabar
</button>
<div style={{ background: '#f0f0f0', padding: '15px', borderRadius: '6px' }}>
<p><strong>Tiesioginiai matmenys:</strong></p>
<ul style={{ listStyleType: 'none', padding: 0, textAlign: 'left', margin: '0 auto', maxWidth: '300px' }}>
<li>Plotis: <b>{width}px</b></li>
<li>Aukštis: <b>{height}px</b></li>
<li>Viršutinė pozicija (peržiūros srityje): <b>{top}px</b></li>
<li>Kairinė pozicija (peržiūros srityje): <b>{left}px</b></li>
</ul>
<p><em>Tikslus elemento matavimas yra labai svarbus prisitaikantiems dizainams ir našumo optimizavimui įvairiuose įrenginiuose visame pasaulyje.</em></p>
</div>
</div>
);
}
}
Šis komponentas naudoja createRef, kad gautų getBoundingClientRect() div elementui, pateikdamas jo realaus laiko matmenis ir padėtį. Ši informacija yra neįkainojama įgyvendinant sudėtingus išdėstymo koregavimus, nustatant matomumą virtualizuotame slinkties sąraše ar net užtikrinant, kad elementai būtų konkrečioje peržiūros srityje. Globaliai auditorijai, kur ekranų dydžiai, skiriamosios gebos ir naršyklių aplinkos labai skiriasi, tikslus išdėstymo valdymas, pagrįstas realiais DOM matavimais, yra pagrindinis veiksnys teikiant nuoseklią ir aukštos kokybės vartotojo patirtį.
Geriausios praktikos ir įspėjimai naudojant `createRef`
Nors createRef siūlo galingą imperatyvią kontrolę, netinkamas jo naudojimas gali lemti sunkiai valdomą ir derinamo kodą. Geriausių praktikų laikymasis yra būtinas norint atsakingai panaudoti jo galią.
1. Teikite pirmenybę deklaratyviems metodams: Auksinė taisyklė
Visada prisiminkite, kad „refs“ yra „avarinis išėjimas“, o ne pagrindinis sąveikos būdas „React“. Prieš griebdamiesi „ref“, paklauskite savęs: ar tai galima pasiekti su būsena ir props? Jei atsakymas yra „taip“, tai beveik visada yra geresnis, labiau „React-idiomatinis“ metodas. Pavyzdžiui, jei norite pakeisti įvesties lauko reikšmę, naudokite valdomus komponentus su būsena, o ne „ref“, kad tiesiogiai nustatytumėte inputRef.current.value.
2. „Refs“ skirti imperatyvioms sąveikoms, o ne būsenos valdymui
„Refs“ geriausiai tinka užduotims, kurios apima tiesioginius, imperatyvius veiksmus su DOM elementais ar komponentų egzemplioriais. Tai yra komandos: „fokusuok šį įvesties lauką“, „grok šį vaizdo įrašą“, „nuslink į šį skyrių“. Jie nėra skirti keisti komponento deklaratyvią UI, remiantis būsena. Tiesioginis elemento stiliaus ar turinio manipuliavimas per „ref“, kai tai galėtų būti valdoma props ar būsena, gali lemti, kad „React“ virtualus DOM atsiskirs nuo realaus DOM, sukeldamas nenuspėjamą elgesį ir atvaizdavimo problemas.
3. „Refs“ ir funkciniai komponentai: Priimkite `useRef` ir `forwardRef`
Šiuolaikiniame „React“ kūrime funkciniuose komponentuose React.createRef() nėra įrankis, kurį naudosite. Vietoj to, jūs pasikliausite useRef kabliuku. useRef kabliukas suteikia kintamą „ref“ objektą, panašų į createRef, kurio .current savybė gali būti naudojama toms pačioms imperatyvioms sąveikoms. Jis išlaiko savo reikšmę per komponento perpiešimus, nesukeldamas paties perpiešimo, todėl puikiai tinka laikyti nuorodą į DOM mazgą ar bet kokią kintamą reikšmę, kuri turi išlikti per atvaizdavimus.
import React, { useRef, useEffect } from 'react';
function FunctionalComponentWithRef() {
const myInputRef = useRef(null); // Inicializuojame su null
useEffect(() => {
// Tai įvykdoma po komponento prijungimo
if (myInputRef.current) {
myInputRef.current.focus();
console.log('Functional component input focused!');
}
}, []); // Tuščias priklausomybių masyvas užtikrina, kad tai įvyks tik vieną kartą prijungus
const handleLogValue = () => {
if (myInputRef.current) {
alert(`Input value: ${myInputRef.current.value}`);
}
};
return (
<div style={{ margin: '20px', padding: '20px', border: '1px solid #009688', borderRadius: '8px', background: '#e0f2f1' }}>
<h3>useRef naudojimas funkciniame komponente</h3>
<label htmlFor="funcInput">Įveskite ką nors:</label><br />
<input id="funcInput" type="text" ref={myInputRef} placeholder="Aš esu automatiškai fokusuotas!" style={{ padding: '8px', margin: '10px 0', borderRadius: '4px', border: '1px solid #ccc' }} /><br />
<button onClick={handleLogValue} style={{ padding: '10px 15px', background: '#009688', color: 'white', border: 'none', borderRadius: '5px', cursor: 'pointer' }}>
Registruoti įvesties reikšmę
</button>
<p><em>Naujiems projektams `useRef` yra idiomatinis pasirinkimas `refs` naudojimui funkciniuose komponentuose.</em></p>
</div>
);
}
Jei jums reikia, kad tėvinis komponentas gautų „ref“ į DOM elementą viduje funkcinio vaikinio komponento, tuomet React.forwardRef yra jūsų sprendimas. Tai aukštesnės eilės komponentas, leidžiantis jums „perduoti“ „ref“ iš tėvinio komponento vienam iš jo vaikinio DOM elementų, išlaikant funkcinio komponento inkapsuliaciją, bet vis tiek leidžiant imperatyvią prieigą, kai to reikia.
import React, { useRef, useEffect } from 'react';
// Funkcinis komponentas, kuris aiškiai perduoda ref savo vietiniam įvesties elementui
const ForwardedInput = React.forwardRef((props, ref) => (
<input type="text" ref={ref} className="forwarded-input" placeholder={props.placeholder} style={{ padding: '10px', margin: '8px 0', border: '1px solid #ccc', borderRadius: '4px', width: '100%' }} />
));
class ParentComponentUsingForwardRef extends React.Component {
constructor(props) {
super(props);
this.parentInputRef = React.createRef();
}
componentDidMount() {
if (this.parentInputRef.current) {
this.parentInputRef.current.focus();
console.log('Input inside functional component focused from parent (class component) via forwarded ref!');
}
}
render() {
return (
<div style={{ margin: '20px', padding: '20px', border: '1px solid #6f42c1', borderRadius: '8px', background: '#f5eef9' }}>
<h3>Ref perdavimo pavyzdys su createRef (Tėvinis klasės komponentas)</h3>
<label>Įveskite duomenis:</label>
<ForwardedInput ref={this.parentInputRef} placeholder="Šis įvesties laukas yra funkciniame komponente" />
<p><em>Šis modelis yra labai svarbus kuriant daugkartinio naudojimo komponentų bibliotekas, kurios turi atverti tiesioginę DOM prieigą.</em></p>
</div>
);
}
}
Tai demonstruoja, kaip klasės komponentas, naudojantis createRef, gali efektyviai sąveikauti su DOM elementu, esančiu funkciniame komponente, pasinaudojant forwardRef. Dėl to funkciniai komponentai tampa lygiaverčiai pajėgūs dalyvauti imperatyviose sąveikose, kai to prireikia, užtikrinant, kad modernūs „React“ kodų rinkiniai vis tiek galėtų pasinaudoti „refs“ teikiamais privalumais.
4. Kada nenaudoti „Refs“: Išlaikant „React“ vientisumą
- Vaikinio komponento būsenos valdymui: Niekada nenaudokite „ref“ tiesiogiai skaityti ar atnaujinti vaikinio komponento būseną. Tai apeina „React“ būsenos valdymą, todėl jūsų programa tampa nenuspėjama. Vietoj to, perduokite būseną žemyn kaip props ir naudokite atgalinio ryšio funkcijas, kad vaikiniai komponentai galėtų prašyti būsenos pakeitimų iš tėvinių.
- Kaip props pakaitalą: Nors jūs galite kviesti metodus vaikinio klasės komponente per „ref“, apsvarstykite, ar perduodant įvykių tvarkyklę kaip prop vaikiniam komponentui nebūtų pasiektas tas pats tikslas labiau „React-idiomatiniu“ būdu. Props skatina aiškų duomenų srautą ir daro komponentų sąveikas skaidrias.
-
Paprastoms DOM manipuliacijoms, kurias gali atlikti „React“: Jei norite pakeisti elemento tekstą, stilių ar pridėti/pašalinti klasę remiantis būsena, darykite tai deklaratyviai. Pavyzdžiui, norėdami perjungti klasę
active, sąlygiškai pritaikykite ją JSX:<div className={isActive ? 'active' : ''}>, o nedivRef.current.classList.add('active').
5. Našumo aspektai ir globalus pasiekiamumas
Nors pats createRef yra našus, operacijos, atliekamos naudojant current, gali turėti didelį poveikį našumui. Vartotojams su silpnesniais įrenginiais ar lėtesniais interneto ryšiais (kas yra įprasta daugelyje pasaulio šalių), neefektyvios DOM manipuliacijos gali sukelti strigimą, nereaguojančias vartotojo sąsajas ir prastą vartotojo patirtį. Naudojant „refs“ užduotims, tokioms kaip animacijos, sudėtingi išdėstymo skaičiavimai ar sunkių trečiųjų šalių bibliotekų integravimas:
-
Debounce/Throttle įvykiai: Jei naudojate „refs“ matmenims matuoti per
window.resizearscrollįvykius, užtikrinkite, kad šios tvarkyklės būtų apdorojamos su „debounce“ ar „throttle“ technikomis, kad išvengtumėte pernelyg dažno funkcijų kvietimo ir DOM skaitymo. -
Grupuokite DOM skaitymus/rašymus: Venkite maišyti DOM skaitymo operacijų (pvz.,
getBoundingClientRect()) su DOM rašymo operacijomis (pvz., stilių nustatymu). Tai gali sukelti „layout thrashing“. Įrankiai, tokie kaipfastdom, gali padėti tai efektyviai valdyti. -
Atidėkite nekritines operacijas: Naudokite
requestAnimationFrameanimacijoms irsetTimeout(..., 0)arrequestIdleCallbackmažiau kritinėms DOM manipuliacijoms, kad užtikrintumėte, jog jos neužblokuos pagrindinės gijos ir nepaveiks reagavimo. - Rinkitės išmintingai: Kartais trečiosios šalies bibliotekos našumas gali būti kliūtis. Įvertinkite alternatyvas arba apsvarstykite tokių komponentų tingųjį įkėlimą (lazy-loading) vartotojams su lėtesniais ryšiais, užtikrinant, kad bazinė patirtis išliktų našia visame pasaulyje.
`createRef` vs. Callback Refs vs. `useRef`: Išsamus palyginimas
„React“ per savo evoliuciją siūlė skirtingus būdus valdyti „refs“. Suprasti kiekvieno niuansus yra raktas į tinkamiausio metodo pasirinkimą jūsų konkrečiam kontekstui.
1. `React.createRef()` (Klasės komponentai - modernus)
-
Mechanizmas: Sukuria „ref“ objektą (
{ current: null }) komponento egzemplioriaus konstruktoriuje. „React“ priskiria DOM elementą ar komponento egzempliorių.currentsavybei po prijungimo. - Pagrindinis naudojimas: Išskirtinai klasės komponentuose. Jis inicializuojamas vieną kartą per komponento egzempliorių.
-
Ref užpildymas:
.currentnustatomas į elementą/egzempliorių po komponento prijungimo ir atstatomas įnullatjungus. - Geriausiai tinka: Visiems standartiniams „ref“ reikalavimams klasės komponentuose, kur reikia nurodyti DOM elementą ar vaikinio klasės komponento egzempliorių.
- Privalumai: Aiški, paprasta objektinio programavimo sintaksė. Nėra problemų dėl įterptinių funkcijų perkūrimo, sukeliančio papildomus iškvietimus (kaip gali atsitikti su atgalinio ryšio „refs“).
- Trūkumai: Negalima naudoti su funkciniais komponentais. Jei neinicijuotas konstruktoriuje (pvz., „render“ metode), naujas „ref“ objektas gali būti sukurtas kiekvieno atvaizdavimo metu, kas gali sukelti našumo problemų ar neteisingų „ref“ reikšmių. Reikia nepamiršti priskirti egzemplioriaus savybei.
2. Atgalinio ryšio „Refs“ (Callback Refs) (Klasės ir funkciniai komponentai - lankstus/senesnis)
-
Mechanizmas: Perduodate funkciją tiesiogiai
refprop'ui. „React“ iškviečia šią funkciją su prijungtu DOM elementu ar komponento egzemplioriumi, o vėliau sunull, kai jis atjungiamas. -
Pagrindinis naudojimas: Gali būti naudojamas tiek klasės, tiek funkciniuose komponentuose. Klasės komponentuose atgalinio ryšio funkcija paprastai yra pririšama prie
thisarba apibrėžiama kaip rodyklinės funkcijos klasės savybė. Funkciniuose komponentuose ji dažnai apibrėžiama vietoje arba memoizuojama. -
Ref užpildymas: Atgalinio ryšio funkciją tiesiogiai iškviečia „React“. Jūs esate atsakingi už nuorodos saugojimą (pvz.,
this.myInput = element;). -
Geriausiai tinka: Scenarijams, reikalaujantiems smulkesnio valdymo, kada „refs“ yra nustatomi ir atstatomi, arba pažangesniems modeliams, tokiems kaip dinaminiai „ref“ sąrašai. Tai buvo pagrindinis būdas valdyti „refs“ prieš
createRefiruseRef. - Privalumai: Suteikia maksimalų lankstumą. Suteikia tiesioginę prieigą prie „ref“, kai jis tampa prieinamas (atgalinio ryšio funkcijoje). Gali būti naudojamas saugoti „refs“ masyve ar žemėlapyje dinaminiams elementų rinkiniams.
-
Trūkumai: Jei atgalinio ryšio funkcija apibrėžta vietoje
rendermetode (pvz.,ref={el => this.myRef = el}), ji bus iškviesta du kartus atnaujinimų metu (vieną kartą sunull, tada su elementu), kas gali sukelti našumo problemų ar netikėtų šalutinių poveikių, jei nebus atsargiai tvarkoma (pvz., paverčiant atgalinio ryšio funkciją klasės metodu arba naudojantuseCallbackfunkciniuose komponentuose).
class CallbackRefDetailedExample extends React.Component {
constructor(props) {
super(props);
this.inputElement = null;
}
// Šį metodą iškvies React, kad nustatytų ref
setInputElementRef = element => {
if (element) {
console.log('Ref element is:', element);
}
this.inputElement = element; // Saugome realų DOM elementą
};
componentDidMount() {
if (this.inputElement) {
this.inputElement.focus();
}
}
render() {
return (
<div>
<label>Atgalinio ryšio Ref įvestis:</label>
<input type="text" ref={this.setInputElementRef} />
</div>
);
}
}
3. `useRef` kabliukas (Funkciniai komponentai - modernus)
-
Mechanizmas: „React“ kabliukas, kuris grąžina kintamą „ref“ objektą (
{ current: initialValue }). Grąžintas objektas išlieka per visą funkcinio komponento gyvavimo laiką. - Pagrindinis naudojimas: Išskirtinai funkciniuose komponentuose.
-
Ref užpildymas: Panašiai kaip ir
createRef, „React“ priskiria DOM elementą ar komponento egzempliorių (jei perduotas).currentsavybei po prijungimo ir nustato jį įnullatjungus..currentreikšmė taip pat gali būti atnaujinta rankiniu būdu. - Geriausiai tinka: Visam „ref“ valdymui funkciniuose komponentuose. Taip pat naudingas laikyti bet kokią kintamą reikšmę, kuri turi išlikti per atvaizdavimus nesukeliant perpiešimo (pvz., laikmačio ID, ankstesnės reikšmės).
- Privalumai: Paprastas, idiomatinis kabliukams. „Ref“ objektas išlieka per atvaizdavimus, išvengiant perkūrimo problemų. Gali saugoti bet kokią kintamą reikšmę, ne tik DOM mazgus.
-
Trūkumai: Veikia tik funkciniuose komponentuose. Reikalingas aiškus
useEffectgyvavimo ciklo susijusioms „ref“ sąveikoms (pvz., fokusavimui prijungus).
Apibendrinant:
-
Jei rašote klasės komponentą ir jums reikia „ref“,
React.createRef()yra rekomenduojamas ir aiškiausias pasirinkimas. -
Jei rašote funkcinį komponentą ir jums reikia „ref“,
useRefkabliukas yra modernus, idiomatinis sprendimas. - Atgalinio ryšio „refs“ vis dar galioja, bet paprastai yra sudėtingesni ir linkę į subtilias problemas, jei nėra atsargiai įgyvendinami. Jie naudingi pažangiems scenarijams arba dirbant su senesniais kodų rinkiniais ar kontekstais, kur kabliukai nėra prieinami.
-
Perduodant „refs“ per komponentus (ypač funkcinius),
React.forwardRef()yra būtinas, dažnai naudojamas kartu sucreateRefaruseReftėviniame komponente.
Globalūs aspektai ir pažangus prieinamumas su „Refs“
Nors dažnai aptariami techniniame vakuume, „refs“ naudojimas globaliai orientuotoje programos kontekste turi svarbių pasekmių, ypač susijusių su našumu ir prieinamumu įvairiems vartotojams.
1. Našumo optimizavimas įvairiems įrenginiams ir tinklams
Pats createRef poveikis paketo dydžiui yra minimalus, nes tai maža „React“ branduolio dalis. Tačiau operacijos, kurias atliekate su current savybe, gali turėti didelį poveikį našumui. Vartotojams su silpnesniais įrenginiais ar lėtesniais interneto ryšiais (kas yra įprasta daugelyje pasaulio šalių), neefektyvios DOM manipuliacijos gali sukelti strigimą, nereaguojančias vartotojo sąsajas ir prastą vartotojo patirtį. Naudojant „refs“ užduotims, tokioms kaip animacijos, sudėtingi išdėstymo skaičiavimai ar sunkių trečiųjų šalių bibliotekų integravimas:
-
Debounce/Throttle įvykiai: Jei naudojate „refs“ matmenims matuoti per
window.resizearscrollįvykius, užtikrinkite, kad šios tvarkyklės būtų apdorojamos su „debounce“ ar „throttle“ technikomis, kad išvengtumėte pernelyg dažno funkcijų kvietimo ir DOM skaitymo. -
Grupuokite DOM skaitymus/rašymus: Venkite maišyti DOM skaitymo operacijų (pvz.,
getBoundingClientRect()) su DOM rašymo operacijomis (pvz., stilių nustatymu). Tai gali sukelti „layout thrashing“. Įrankiai, tokie kaipfastdom, gali padėti tai efektyviai valdyti. -
Atidėkite nekritines operacijas: Naudokite
requestAnimationFrameanimacijoms irsetTimeout(..., 0)arrequestIdleCallbackmažiau kritinėms DOM manipuliacijoms, kad užtikrintumėte, jog jos neužblokuos pagrindinės gijos ir nepaveiks reagavimo. - Rinkitės išmintingai: Kartais trečiosios šalies bibliotekos našumas gali būti kliūtis. Įvertinkite alternatyvas arba apsvarstykite tokių komponentų tingųjį įkėlimą (lazy-loading) vartotojams su lėtesniais ryšiais, užtikrinant, kad bazinė patirtis išliktų našia visame pasaulyje.
2. Prieinamumo gerinimas (ARIA atributai ir klaviatūros navigacija)
„Refs“ yra labai svarbūs kuriant labai prieinamas interneto programas, ypač kuriant pasirinktinius UI komponentus, kurie neturi vietinių naršyklės atitikmenų, arba keičiant numatytąjį elgesį. Globaliai auditorijai, Web Content Accessibility Guidelines (WCAG) laikymasis yra ne tik gera praktika, bet dažnai ir teisinis reikalavimas. „Refs“ leidžia:
- Programinis fokusavimo valdymas: Kaip matėme su įvesties laukais, „refs“ leidžia nustatyti fokusą, kas yra labai svarbu klaviatūros vartotojams ir ekrano skaitytuvų navigacijai. Tai apima fokusavimo valdymą modaluose, išskleidžiamuosiuose meniu ar interaktyviuose valdikliuose.
-
Dinaminiai ARIA atributai: Galite naudoti „refs“, kad dinamiškai pridėtumėte ar atnaujintumėte ARIA (Accessible Rich Internet Applications) atributus (pvz.,
aria-expanded,aria-controls,aria-live) DOM elementuose. Tai suteikia semantinę informaciją pagalbinėms technologijoms, kurios gali būti neįmanoma nuspėti vien iš vizualinės UI.class CollapsibleSection extends React.Component {
constructor(props) {
super(props);
this.buttonRef = React.createRef();
this.state = { isExpanded: false };
}
toggleExpanded = () => {
this.setState(prevState => ({ isExpanded: !prevState.isExpanded }), () => {
if (this.buttonRef.current) {
// Dinamiškai atnaujinti ARIA atributą remiantis būsena
this.buttonRef.current.setAttribute('aria-expanded', this.state.isExpanded);
}
});
};
componentDidMount() {
if (this.buttonRef.current) {
this.buttonRef.current.setAttribute('aria-controls', `section-${this.props.id}`);
this.buttonRef.current.setAttribute('aria-expanded', this.state.isExpanded);
}
}
render() {
const { id, title, children } = this.props;
const { isExpanded } = this.state;
return (
<div style={{ margin: '20px auto', maxWidth: '600px', border: '1px solid #0056b3', borderRadius: '8px', background: '#e7f0fa', overflow: 'hidden' }}>
<h4>
<button
ref={this.buttonRef} // Ref mygtukui ARIA atributams
onClick={this.toggleExpanded}
style={{ background: 'none', border: 'none', padding: '15px 20px', width: '100%', textAlign: 'left', cursor: 'pointer', fontSize: '1.2em', color: '#0056b3', display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}
id={`section-header-${id}`}
>
{title} <span>▼</span>
</button>
</h4>
{isExpanded && (
<div id={`section-${id}`} role="region" aria-labelledby={`section-header-${id}`} style={{ padding: '0 20px 20px', borderTop: '1px solid #a7d9f7' }}>
{children}
</div>
)}
</div>
);
}
} - Klaviatūros sąveikos kontrolė: Pasirinktiniams išskleidžiamiesiems meniu, slankikliams ar kitiems interaktyviems elementams gali prireikti įgyvendinti specifinius klaviatūros įvykių tvarkytojus (pvz., rodyklių klavišus navigacijai sąraše). „Refs“ suteikia prieigą prie tikslinio DOM elemento, kur šie įvykių klausytojai gali būti prijungti ir valdomi.
Apgalvotai taikydami „refs“, programuotojai gali užtikrinti, kad jų programos būtų naudojamos ir įtraukiančios žmonėms su negalia visame pasaulyje, taip smarkiai išplečiant jų globalų pasiekiamumą ir poveikį.
3. Internacionalizacija (I18n) ir lokalizuotos sąveikos
Dirbant su internacionalizacija (i18n), „refs“ gali atlikti subtilų, bet svarbų vaidmenį. Pavyzdžiui, kalbose, kurios naudoja iš dešinės į kairę (RTL) raštą (pvz., arabų, hebrajų ar persų), natūrali tabuliavimo tvarka ir slinkimo kryptis gali skirtis nuo iš kairės į dešinę (LTR) kalbų. Jei programiškai valdote fokusą ar slinkimą naudodami „refs“, labai svarbu užtikrinti, kad jūsų logika atsižvelgtų į dokumento ar elemento teksto kryptį (dir atributą).
- RTL pritaikytas fokusavimo valdymas: Nors naršyklės paprastai teisingai tvarko numatytąją tabuliavimo tvarką RTL, jei įgyvendinate pasirinktinius fokusavimo spąstus ar nuoseklų fokusavimą, kruopščiai išbandykite savo „ref“ pagrįstą logiką RTL aplinkose, kad užtikrintumėte nuoseklią ir intuityvią patirtį.
-
Išdėstymo matavimas RTL: Naudodami
getBoundingClientRect()per „ref“, atminkite, kadleftirrightsavybės yra santykinės peržiūros srities atžvilgiu. Išdėstymo skaičiavimams, kurie priklauso nuo vizualinės pradžios/pabaigos, atsižvelkite įdocument.dirarba elemento apskaičiuotą stilių, kad pritaikytumėte savo logiką RTL išdėstymams. - Trečiųjų šalių bibliotekų integracija: Užtikrinkite, kad visos trečiųjų šalių bibliotekos, integruotos per „refs“ (pvz., diagramų bibliotekos), pačios būtų pritaikytos i18n ir teisingai tvarkytų RTL išdėstymus, jei jūsų programa juos palaiko. Atsakomybė už tai dažnai tenka programuotojui, integruojančiam biblioteką į „React“ komponentą.
Išvada: Imperatyvaus valdymo įvaldymas su `createRef` globalioms programoms
React.createRef() yra daugiau nei tik „avarinis išėjimas“ „React“; tai gyvybiškai svarbus įrankis, kuris užpildo atotrūkį tarp galingos „React“ deklaratyvios paradigmos ir imperatyvių naršyklės DOM sąveikų realybės. Nors jo vaidmenį naujesniuose funkciniuose komponentuose didžiąja dalimi perėmė useRef kabliukas, createRef išlieka standartiniu ir labiausiai idiomatiniu būdu valdyti „refs“ klasės komponentuose, kurie vis dar sudaro didelę dalį daugelio įmonių lygio programų visame pasaulyje.
Išsamiai suprasdami jo sukūrimą, prijungimą ir kritinį .current savybės vaidmenį, programuotojai gali užtikrintai spręsti tokius iššūkius kaip programinis fokusavimo valdymas, tiesioginis medijos valdymas, sklandi integracija su įvairiomis trečiųjų šalių bibliotekomis (nuo D3.js diagramų iki pasirinktinių teksto redaktorių) ir tikslus elementų matmenų matavimas. Šios galimybės yra ne tik techniniai pasiekimai; jos yra fundamentalios kuriant programas, kurios yra našios, prieinamos ir patogios vartotojui plačiame globalių vartotojų, įrenginių ir kultūrinių kontekstų spektre.
Nepamirškite šią galią naudoti protingai. Visada pirmenybę teikite „React“ deklaratyviai būsenos ir prop sistemos. Kai imperatyvus valdymas yra tikrai reikalingas, createRef (klasės komponentams) arba useRef (funkciniams komponentams) siūlo tvirtą ir gerai apibrėžtą mechanizmą tai pasiekti. Įvaldę „refs“, jūs įgalinate save tvarkytis su kraštutiniais atvejais ir šiuolaikinio interneto kūrimo subtilybėmis, užtikrindami, kad jūsų „React“ programos galėtų teikti išskirtines vartotojo patirtis bet kurioje pasaulio vietoje, išlaikant pagrindinius „React“ elegantiškos, komponentais pagrįstos architektūros privalumus.
Tolimesnis mokymasis ir tyrinėjimas
- Oficiali „React“ dokumentacija apie „Refs“: Norėdami gauti naujausią informaciją tiesiai iš šaltinio, apsilankykite <em>https://react.dev/learn/manipulating-the-dom-with-refs</em>
- Supratimas apie „React“ `useRef` kabliuką: Norėdami giliau pasinerti į funkcinio komponento atitikmenį, tyrinėkite <em>https://react.dev/reference/react/useRef</em>
- „Ref“ perdavimas su `forwardRef`: Sužinokite, kaip efektyviai perduoti „refs“ per komponentus: <em>https://react.dev/reference/react/forwardRef</em>
- Interneto turinio prieinamumo gairės (WCAG): Būtina globaliam interneto kūrimui: <em>https://www.w3.org/WAI/WCAG22/quickref/</em>
- „React“ našumo optimizavimas: Geriausios praktikos didelio našumo programoms: <em>https://react.dev/learn/optimizing-performance</em>